using System;
using System.Threading;
using System.Text;

namespace netdreamo
{
    class GPSDataStream
    {

        private System.IO.Ports.SerialPort gpsSerial;
        private String stringBuffer;
        private Timer timer;

        public delegate void GPSSentenceReady(GPSDataStream sender, String sentence);

        public event GPSSentenceReady SentenceIsReady;

        public GPSDataStream(System.IO.Ports.SerialPort gpsSerial, int checkForDataInMillis)
        {

            this.gpsSerial = gpsSerial;
            gpsSerial.Open();

            stringBuffer = "";

            if (checkForDataInMillis > 0) //Set up auto check
            {
                timer = new Timer(new TimerCallback(CheckGPSData), null, checkForDataInMillis, checkForDataInMillis);
            }

        }

        public void CheckGPSData()
        {
            CheckGPSData(null);
        }

        /// <summary>
        /// Checks the com to see if there is any gps data available.
        /// </summary>
        public void CheckGPSData(Object obj)
        {
            if (gpsSerial.BytesToRead <= 0)
                return;

            int bytesToRead = gpsSerial.BytesToRead;

            byte[] buffer = new byte[bytesToRead];

            int bytesRead = gpsSerial.Read(buffer, 0, bytesToRead);

            //this will lock (or pause) until this object is unlocked
            lock (this)
            {
                if (bytesRead > 0)
                {
                    stringBuffer += GetAllValidChars(buffer, bytesRead);
                }
            }
            if (this.SentenceIsReady != null)
            {
                String str = "";
                str = this.GetGPSSentence();
                while (str != "")
                {
                    SentenceIsReady(this, str);
                    str = this.GetGPSSentence();
                }
            }

            
        }

        /// <summary>
        /// Goes through the GPS data and removes all invalid characters and returns a valid string.
        /// </summary>
        /// <param name="buffer">a byte array of data received from a gps device</param>
        /// <param name="bytesToConvert">how many bytes to deal with</param>
        /// <returns>A string with all the valid characters from the buffer</returns>
        private string GetAllValidChars(byte[] buffer, int bytesToConvert)
        {
            byte upper = 128;

            char[] tmpret = new char[bytesToConvert];
            int curLoc = 0;
            for (int i = 0; i < bytesToConvert; i++)
            {
                if (buffer[i] < upper)
                {
                    tmpret[curLoc++] = (char)buffer[i];
                }
            }
            
            return new string(tmpret, 0, curLoc); //create a string of length curLoc and copy the tmpret array into it
        }

        /// <summary>
        /// Locks the current object and pulls out the gps string.
        /// </summary>
        /// <returns>The GPS String</returns>
        public String GetGPSSentence()
        {
            lock (this)
            {
                int start = stringBuffer.IndexOf('$');
                if (start >= 0)
                {
                    stringBuffer = stringBuffer.Substring(start);
                    start = stringBuffer.IndexOf('$');
                    int end = stringBuffer.IndexOf("*", start) + 3;

                    if (stringBuffer.Length > end && end != 2) //*XX  make sure those extra parts of the thing exist  
                    {
                        String parsedText = stringBuffer.Substring(start, (end - start));
                        stringBuffer = stringBuffer.Substring(end); //end + 1 ?
                        return parsedText;
                    }
                }
            }

            return "";
        }
    }
}